package com.cskaoyan.javase.oop3._3lambda._1basic;

import java.util.Arrays;

/**
 * Lambda表达式的进阶使用,Lambda表达式的简化
 * 从实践开发的角度出发,Lambda表达式必然是可以写成一行的
 *
 * Lambda表达式能够简化的前提是
 * 它是功能接口的子类对象，而功能接口中有且只有一个必须要实现的抽象方法！
 * 并且Lambda表达式的简化,不能给Lambda表达式带来歧义,否则就不能简化!
 *
 * 基于下列Lambda表达式的语法：
 * (形参列表) -> {
 *  // 方法体
 * }
 *
 * 思考以下问题,逐一简化Lambda表达式的每个语法结构:
 *      1."(形参列表)"可不可以简化?
 *          可以,形参的数据类型完全可以去掉,但是形参的名字肯定要写,因为重写的方法体中要用
 *          特殊的,如果抽象方法是无参方法,这时写"()"不能省略
 *              如果抽象方法是一个单参数的方法,"()"是可以被省略的
 *
 *      2."{}"能不能去掉,能不能简化掉?
 *          可以简化,参考if简化"{}"
 *          当Lambda表达式重写的抽象方法的方法体仅有一行语句时,大括号就可以去掉了
 *          特殊的,如果这一行语句就恰好是方法的返回值语句,那么连带return一起省略,全部干掉.
 *
 *  实际开发中,最需要被简化的是"{}",一行写完的Lambda表达式显然更简洁优雅
 *  但是实际开发中,大多数情况下,重写抽象方法一行写完是几乎不可能的
 *  如果重写抽象方法的语句有很多条,这时就没有办法去掉"{}"了,那怎么办呢?
 *
 *  这时候就需要使用Lambda表达式的语法——"方法引用"
 *  它指的是用一个"已存在的,已实现的方法"作为Lambda表达式表示的功能接口中抽象方法的实现.
 *  以前Lambda表达式的抽象方法实现必须写在"自身语法的{}里",现在不需要写了,直接指向一个已实现的方法
 *
 *  学会这个语法,最关键的问题是: 这个"已实现的方法"和功能接口中的抽象方法有什么关联?
 *
 *  一个方法的定义语法如下:
 *  [修饰符列表] 返回值类型 方法名(形参列表){
 *      // 方法体
 *  }
 *
 *  1.对"已实现的方法"的访问权限修饰符有要求吗?
 *  有要求,但是只需要在使用的位置有该方法的访问权限即可,不强制要求是什么访问权限.
 *
 *  2.对"已实现的方法"static还是非static有要求吗?
 *  没有要求,无所谓,static还是非static只影响方法调用的方式,不影响方法的功能实现
 *
 *  3.对"已实现的方法"返回值类型有要求吗?
 *  有要求
 *  "已实现的方法"的返回值类型必须和抽象方法保持一致，当抽象方法的返回值类型是基本数据类型和void时
 *  当抽象方法的返回值类型时一个父类类型时
 *  这时"已实现的方法"可以返回一个子类类型(当然也可以保持一致)
 *
 *  4.对"已实现的方法"形参列表有要求吗?
 *  有要求
 *  必须保持完全一致
 *
 *  5.对"已实现的方法"方法体有要求吗?
 *  没有要求,可以随意写
 *
 *  6.对"已实现的方法"的方法名有要求吗?
 *  很明显是没有要求的
 *
 * 方法引用的语法:
 * 第一种方式,不省略"->"带着"->"的语法形式(不推荐)
 * (形参列表) -> 已实现方法的调用(形参列表);
 * 解释:
 *      1.前后的形参列表都是照抄抽象方法的形参列表,而且前后必须保持一致
 *      2."已实现方法的调用"在这里调用这个已实现的方法,如果是静态方法,用类名.调用,如果是成员方法,用对象名.调用
 *
 * 第二种方式,省略"->" 语法更简洁,更推荐的方式
 * 和类型推断写在一起,语法是
 * 功能接口名 对象名 = 方法的归属者::已实现方法的方法名;
 * 解释:
 *      1.方法的归属者,成员方法属于对象,静态方法属于类
 *      2.方法的归属者后面跟的是两个":"
 *      3.已实现方法的方法名就只写名字,不写任何形参
 *
 *
 * @since 17:35
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        INoReturnTwoParam ip1 = (int num1, int num2) -> System.out.println("这两个数的和是:" + (num1 + num2));
        ip1.test(10, 10);

        // 以上Lambda表达式简化为
        INoReturnTwoParam ip2 = (num1, num2) -> System.out.println("这两个数的差是:" + (num1 - num2));
        ip2.test(200, 10);

        INoReturnOneParam ip3 = num -> System.out.println("这个数就是:" + num);
        ip3.test(999);

        IHasReturnOneParam ip4 = a -> 6 + a;
        System.out.println(ip4.method(100));

        INoReturnNoParam ip5 = () -> {
            System.out.println("hello world!");
            System.out.println("hello world!");
            System.out.println("hello world!");
            System.out.println("hello world!");
            System.out.println("hello world!");
        };

        // 在这里应用方法的引用语法
        INoReturnNoParam ip6 = () -> Demo.myTest();
        ip6.test();

        IHasReturnOneParam ip7 = a -> new Demo().method(a);
        System.out.println(ip7.method(666));


        INoReturnNoParam ip8 = Demo::myTest;
        ip8.test();

        IHasReturnOneParam ip9 = new Demo()::method;
        System.out.println(ip9.method(777));

        System.out.println("----------------------");
        IA ia = A::myTest;
        System.out.println(ia.test(10, 0.1));
        System.out.println("---------------------");

        IA ia2 = new A()::test;
        Object o = ia2.test(10, 10);
        int[] arr = (int[]) o;
        System.out.println(Arrays.toString(arr));
    }

    // 该方法可以用INoReturnNoParam接口下抽象方法的方法引用指向的方法实现
    private static void myTest() {
        System.out.println("666666");
        System.out.println("777777");
        System.out.println("hello world!");
        System.out.println("大家新年快乐!");
    }

    // 该方法可以作为IHasReturnOneParam接口下抽象方法的方法引用指向的方法实现
    public int method(int num) {
        System.out.println("---------------------");
        System.out.println("我喜欢看篮球比赛!");
        System.out.println("---------------------");
        return num;
    }

}

class A {
    static String myTest(int num1, double num2) {
        System.out.println(num1 + num2);
        return "hello world!";
    }

    public Object test(int a, double b) {
        System.out.println("66666");
        return new int[3];
    }
}


@FunctionalInterface
interface IA {
    Object test(int a, double b);
}

//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}